package com.tanhua.admin.service;

/**
 * @Author: cl
 * @Date: 2022/5/17 - 05 - 17
 * @Description:
 */

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.tanhua.admin.mapper.AnalysisMapper;
import com.tanhua.admin.mapper.LogMapper;
import com.tanhua.dubbo.api.UserApi;
import com.tanhua.model.domain.Analysis;
import com.tanhua.model.vo.AnalysisUsersVo;
import com.tanhua.model.vo.DataPointVo;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * log统计表(Analysis)服务
 */
@Service
public class AnalysisService {

    //日志表
    @Autowired
    private LogMapper logMapper;

    //日志统计表
    @Autowired
    private AnalysisMapper analysisMapper;

    //用户服务
    @DubboReference
    private UserApi userApi;

    /**
     * 定时统计日志表中的数据
     * 1、查询tb_log表中的数 （每日注册用户数，每日登陆用户，活跃的用户数据，次日留存的用户）
     * 2、构造AnalysisByDay对象
     * 3、完成统计数据的更新或者保存
     */
    public void analysis() throws ParseException {

        //1、定义查询的日期
        String todayStr = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        //hutool工具类获取昨天时间
        String yesterdayStr = DateUtil.yesterday().toString("yyyy-MM-dd");
        //2、注册数量
        Integer regCount = logMapper.getByTypeAndLogTime("0102", todayStr);
        //3、登录数量
        Integer loginCount = logMapper.getByTypeAndLogTime("0101", todayStr);
        //4、活跃数量
        Integer activeCount = logMapper.getByLogTime(todayStr);
        //5、次日留存(昨日注册并今日登录)
        Integer numRetention1d = logMapper.getNumRetention1d(todayStr, yesterdayStr);
        //6、查询日志统计表看是否存在数据
        Analysis analysis = analysisMapper.selectOne(
                Wrappers.<Analysis>lambdaQuery()
                        .eq(Analysis::getRecordDate, new SimpleDateFormat("yyyy-MM-dd").parse(todayStr))
        );
        //7、判断是否存在数据
        if (analysis != null) {
            //存在则更新
            analysis.setNumRegistered(regCount);
            analysis.setNumLogin(loginCount);
            analysis.setNumActive(activeCount);
            analysis.setNumRetention1d(numRetention1d);
            analysis.setUpdated(new Date());
            analysisMapper.updateById(analysis);
        } else {
            //不存在直接保存
            analysis = new Analysis();
            analysis.setNumRegistered(regCount);
            analysis.setNumLogin(loginCount);
            analysis.setNumActive(activeCount);
            analysis.setNumRetention1d(numRetention1d);
            analysis.setRecordDate(new SimpleDateFormat("yyyy-MM-dd").parse(todayStr));
            analysis.setCreated(new Date());
            analysis.setUpdated(new Date());
            analysisMapper.insert(analysis);
        }


    }

    /**
     * 获取总共用户数
     *
     * @return
     */
    public Long getAllUserCount() {

        //调用用户服务
        return userApi.countAllUser();
    }

    /**
     * 指定时间范围内的用户活跃量
     *
     * @param today  指定的时间
     * @param offset 时间的偏移量
     * @return
     */
    public Long getActiveUserCount(DateTime today, int offset) {

        return getAnalysisCount(today, offset, "num_active");//封装时间段数据方法
    }


    /**
     * 查询指定时间活跃用户的数量
     *
     * @param today  指定时间
     * @param offset 指定的哪个时间
     * @return
     */
    public Long getActiveUserCountDay(DateTime today, int offset) {

        //计算昨天的时间
        DateTime thisDay = DateUtil.offsetDay(today, offset);
        Long num = getAnalysisCountDay(thisDay, "num_active");//封装的指定时间的数据方法
        return num == null ? 0 : num;
    }

    /**
     * 指定时间新增用户数量
     *
     * @param today
     * @param offset
     * @return
     */
    public Long getRegisterUserCount(DateTime today, int offset) {
        //计算昨天的时间
        DateTime thisDay = DateUtil.offsetDay(today, offset);
        Long num = getAnalysisCountDay(thisDay, "num_registered");//封装的指定时间的数据方法
        return num == null ? 0 : num;
    }

    /**
     * 指定时间登录用户数量
     *
     * @param today
     * @param offset
     * @return
     */
    public Long getLoginUserCount(DateTime today, int offset) {
        //计算昨天的时间
        DateTime thisDay = DateUtil.offsetDay(today, offset);
        Long num = getAnalysisCountDay(thisDay, "num_login");//封装的指定时间的数据方法
        return num == null ? 0 : num;
    }

    /**
     * 抽取指定时间的数据
     *
     * @param thisDay
     * @param column
     * @return
     */
    private Long getAnalysisCountDay(DateTime thisDay, String column) {

        //转换时间类型
        String thisDate = thisDay.toDateStr();
        //查数据
        Long num = analysisMapper.sumAnalysisDataDay(thisDate, column);
        return num == null ? 0 : num;
    }

    /**
     * 抽取指定时间段的数据
     *
     * @param today
     * @param offset
     * @param column
     * @return
     */
    private Long getAnalysisCount(DateTime today, int offset, String column) {

        //1、今日数据处理
        String todayDate = today.toDateStr();
        //2、时间偏移时间点
        String thatDate = DateUtil.offsetDay(today, offset).toDateStr();

        //3、调用mapper统计数据
        Long num = analysisMapper.sumAnalysisData(today, thatDate, column);
        return num == null ? 0 : num;
    }

    /**
     * 新增、活跃用户、次日留存率
     *
     * @param sd
     * @param ed
     * @param type
     * @return
     */
    public AnalysisUsersVo getAnalysisUsersVo(Long sd, Long ed, Integer type) {
        DateTime startDate = DateUtil.date(sd);
        DateTime endDate = DateUtil.date(ed);

        //vo对象
        AnalysisUsersVo analysisUsersVo = new AnalysisUsersVo();

        //今年数据
        analysisUsersVo.setThisYear(queryDataPointVos(startDate, endDate, type));
        //去年数据
        analysisUsersVo.setLastYear(queryDataPointVos(
                DateUtil.offset(startDate, DateField.YEAR, -1),
                DateUtil.offset(endDate, DateField.YEAR, -1),
                type
        ));
        return analysisUsersVo;
    }

    //封装方法
    private List<DataPointVo> queryDataPointVos(DateTime startDate, DateTime endDate, Integer type) {

        //处理时间类型
        String start = startDate.toDateStr();
        String end = endDate.toDateStr();

        //匹配类型(101 新增 102 活跃用户 103 次日留存率)
        String column = null;
        switch (type) {
            case 101:
                column = "num_registered";
                break;
            case 102:
                column = "num_retention1d";
                break;
            case 103:
                column = "num_active";
                break;
        }

        QueryWrapper<Analysis> wrapper = new QueryWrapper<>();
        wrapper.select("record_date , " + column + " as num_active")//起别名方便收集数据
                .ge("record_date", startDate)
                .le("record_date", endDate);
        List<Analysis> analysisByDayList = analysisMapper.selectList(wrapper);

        //利用stream流处理数据封装数据
        List<DataPointVo> list = analysisByDayList.stream().map(
                analysisByDay -> new DataPointVo(
                        DateUtil.date(analysisByDay.getRecordDate()).toDateStr(),//标题(2022-11-1)
                        analysisByDay.getNumActive().longValue()
                )
        ).collect(Collectors.toList());

        return list;
    }
}
